home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 1.0 beta / flock-1.0RC3.en-US.win32.exe / flock / components / flockPiczoService.js < prev    next >
Text File  |  2007-10-18  |  61KB  |  1,854 lines

  1. // vim: ts=2 sw=2 expandtab cindent
  2. //
  3. // BEGIN FLOCK GPL
  4. // 
  5. // Copyright Flock Inc. 2005-2007
  6. // http://flock.com
  7. // 
  8. // This file may be used under the terms of of the
  9. // GNU General Public License Version 2 or later (the "GPL"),
  10. // http://www.gnu.org/licenses/gpl.html
  11. // 
  12. // Software distributed under the License is distributed on an "AS IS" basis,
  13. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14. // for the specific language governing rights and limitations under the
  15. // License.
  16. // 
  17. // END FLOCK GPL
  18.  
  19. const Cc = Components.classes;
  20. const Ci = Components.interfaces;
  21. const Cr = Components.results;
  22. const Cu = Components.utils;
  23.  
  24. const CLASS_ID = Components.ID("{8DAEC4A0-8623-11DB-B606-0800200C9A66}");
  25. const CLASS_NAME = "piczo";
  26. const CLASS_DESC = "Flock Piczo Service";
  27. const CONTRACT_ID = "@flock.com/people/piczo;1";
  28. const FLOCK_PHOTO_ALBUM_CONTRACTID = "@flock.com/photo-album;1";
  29. const FLOCK_PHOTO_ACCOUNT_CONTRACTID = "@flock.com/photo-account;1";
  30. const SERVICE_ENABLED_PREF = "flock.service.piczo.enabled";
  31. const CATEGORY_COMPONENT_NAME       = "Piczo JS Component"
  32. const CATEGORY_ENTRY_NAME           = "piczo"
  33.  
  34. var gCompTK;
  35. function getCompTK() {
  36.   if (!gCompTK) {
  37.     gCompTK = Components.classes["@flock.com/singleton;1"]
  38.                         .getService(Components.interfaces.flockISingleton)
  39.                         .getSingleton("chrome://browser/content/flock/services/common/load-compTK.js")
  40.                         .wrappedJSObject;
  41.   }
  42.   return gCompTK;
  43. }
  44.  
  45. var gTimers = [];  // For use with the scheduler
  46.  
  47. // String defaults... may be updated later through Web Detective
  48. var gStrings = {
  49.   "domains": "piczo.com",
  50.   "sessioncookie": "JSESSIONID",
  51.   "mainpage": "http://www.piczo.com/",
  52.   "favicon": "http://www.piczo.com/favicon.ico",
  53.   "userlogin": "http://www.piczo.com/",
  54.   "userprofile": "http://%accountid%.piczo.com/",
  55.   "api-agent-string": "Flock",
  56.   "api-setup-URL": "http://api.piczo.com/api-1/setup?api-agent=%agent%",
  57.   "api-login-URL": "http://api.piczo.com/api-1/login",
  58.   "api-friends-URL": "http://api.piczo.com/api-1/friends",
  59. };
  60.  
  61. var gAPI = null;
  62. var gSVC = null;
  63.  
  64. function loadLibraryFromSpec(aSpec)
  65. {
  66.   var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
  67.                          .getService(Components.interfaces.mozIJSSubScriptLoader);
  68.   loader.loadSubScript(aSpec);
  69. }
  70.  
  71. loadLibraryFromSpec("chrome://flock/content/common/flocksafe.js");
  72. loadLibraryFromSpec("chrome://browser/content/flock/photo/photoAPI.js");
  73.  
  74. // ==============================================
  75. // ========== BEGIN piczoService class ==========
  76. // ==============================================
  77. var _logger = null;
  78. function piczoService()
  79. {
  80.   _logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  81.   _logger.init("piczo");
  82.   _logger.info("CONSTRUCTOR");
  83.  
  84.   this.obs = Components.classes["@mozilla.org/observer-service;1"]
  85.                        .getService(Components.interfaces.nsIObserverService);
  86.   this.acUtils = Components.classes["@flock.com/account-utils;1"]
  87.                            .getService(Components.interfaces.flockIAccountUtils);
  88.   this.url = gStrings["mainpage"];
  89.   this.mIsInitialized = false;
  90.   this._ctk = {
  91.     interfaces: [
  92.       "nsISupports",
  93.       "nsIClassInfo",
  94.       "nsISupportsCString",
  95.       "nsIObserver",
  96.       "flockIWebService",
  97.       "flockIManageableWebService",
  98.       "flockIPollingService",
  99.       "flockIAuthenticateNewAccount",
  100.       "flockIMediaWebService",
  101.       "flockIMediaUploadAccount", 
  102.       "flockIPiczoService"
  103.     ],
  104.     shortName: "piczo",
  105.     fullName: "Piczo",
  106.     description: CLASS_DESC,
  107.     domains: gStrings["domains"],
  108.     favicon: gStrings["favicon"],
  109.     CID: CLASS_ID,
  110.     contractID: CONTRACT_ID,
  111.     accountClass: piczoAccount,
  112.   };
  113. }
  114.  
  115. // BEGIN flockIMediaWebService interface
  116. function createEnum( array ) {
  117.   return {
  118.     QueryInterface: function (iid) {
  119.       if (iid.equals(Ci.nsISimpleEnumerator)) {
  120.         return this;
  121.       }
  122.       throw Cr.NS_ERROR_NO_INTERFACE;
  123.     },
  124.     hasMoreElements: function() {
  125.       return (array.length > 0);
  126.     },
  127.     getNext: function() {
  128.       return array.shift();
  129.     }
  130.   }
  131. }
  132.  
  133. function piczoPhoto() {
  134.  
  135. }
  136.  
  137. piczoPhoto.prototype= {
  138.   id: "",
  139.   thumbnail: "",
  140.   webPageUrl: "",
  141.   midSizePhoto: "",
  142.   largeSizePhoto: "",
  143.   title: "",
  144.   username: "",
  145.   userid: "",
  146.   is_public: true,
  147.   is_video: false,
  148.   svcShortName: 'piczo',
  149.   buildTooltip: function( ) {
  150.     // do we have to use document from the window to ceate elements? -- ja
  151.     var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
  152.              getService(Ci.nsIWindowMediator);
  153.     var win = wm.getMostRecentWindow('navigator:browser');
  154.     if (!win) {
  155.       return null;
  156.     }
  157.  
  158.     var hbox = win.document.createElement('hbox');
  159.     var img = win.document.createElement('image');
  160.     img.setAttribute('src', this.icon );
  161.     hbox.appendChild(img);
  162.     var box = win.document.createElement('vbox');
  163.     box.setAttribute('style', 'max-width: 250px');
  164.     var title = win.document.createElement('label');
  165.     title.setAttribute('value', this.title );
  166.     title.setAttribute('crop', 'end');
  167.     box.appendChild(title);
  168.     var lbl = win.document.createElement('label');
  169.     lbl.setAttribute('value', this.username );
  170.     lbl.setAttribute('class', 'user');
  171.     box.appendChild(lbl);
  172.     hbox.appendChild(box)
  173.  
  174.     var vbox = win.document.createElement('vbox');
  175.     var cbox = win.document.createElement('hbox'); 
  176.     var largeImg = win.document.createElement('image');
  177.     largeImg.setAttribute('src', this.midSizePhoto);
  178.     largeImg.setAttribute('style', 'margin-bottom: 2px;');
  179.     var spacer = win.document.createElement('spacer');
  180.     spacer.setAttribute('flex', '1');
  181.     cbox.appendChild(largeImg);
  182.     cbox.appendChild(spacer);
  183.     vbox.appendChild(cbox);
  184.     vbox.appendChild(hbox);
  185.  
  186.     return vbox;
  187.   },
  188.   buildHTML: function( ) {
  189.     return '<a title="'+this.title+'" href="'+this.webPageUrl+'"><img src="'+this.midSizePhoto+'" border="0" /></a>';
  190.   },
  191.   buildLargeHTML: function( ) {
  192.     return '<a title="'+this.title+'" href="'+this.webPageUrl+'"><img src="'+this.largeSizePhoto+'" border="0" /></a>';
  193.   },
  194.   buildBBCode: function ( ) {
  195.     return '[url=' + this.webPageUrl + '][img]'+ this.largeSizePhoto +'[/img][/url]';
  196.   },
  197.   QueryInterface: function(iid) {
  198.     if (!iid.equals(Ci.nsISupports) && !iid.equals(Ci.flockIPhoto)) {
  199.       throw Cr.NS_ERROR_NO_INTERFACE;
  200.     }
  201.     return this;
  202.   }
  203. };
  204.  
  205. piczoService.prototype.iconUrl = gStrings["favicon"];
  206.  
  207. piczoService.prototype.handleDragDrop = 
  208. function piczoService_handleDragDrop (aURL, aXLoc, aYLoc) {
  209.   return gAPI.handleDragDrop(aURL, aXLoc, aYLoc);
  210. }
  211.  
  212. piczoService.prototype.supportsFeature = 
  213. function piczoService_supportsFeature(aFeature)
  214. {
  215.   var supports = {};
  216.   supports.tags = false;
  217.   supports.title = false;
  218.   supports.notes = false;
  219.   supports.fileName = false;
  220.   supports.contacts = true;
  221.   supports.privacy = false;
  222.   supports.albumCreation = false;
  223.   return (supports[aFeature] == true);
  224. }
  225.  
  226. piczoService.prototype.login =
  227. function piczoService_login(aAccountURN, aListener) {
  228.   _logger.info("piczoService.login()");
  229. }
  230.  
  231. piczoService.prototype.__defineGetter__('isLoggedIn', function () { return gAPI.isLoggedIn(); })
  232.  
  233. piczoService.prototype.getAuthPerson = function () {
  234. try {
  235.     var session = gAPI.getAuthUser();
  236.     var person = {};
  237.     person.id = session.userGUID;
  238.     person.fullname = session.username;
  239.     person.username = session.username;
  240.     person.service = this;
  241.     return person;
  242.   }
  243.   catch(e) {
  244.     return null;
  245.   }
  246. }
  247.  
  248. piczoService.prototype.findByUsername = 
  249. function piczoService_findByUsername(aListener, aUsername) {
  250.   // Needed to have observer call .search();
  251.   aListener.onError(null);
  252. }
  253.  
  254. piczoService.prototype.serviceName = "Piczo";
  255. piczoService.prototype.shortName = "piczo";
  256.  
  257. piczoService.prototype.search =
  258. function piczoService_search(aListener, aQueryString, aCount, aPage)
  259. {
  260.   // Piczo doesn't support paging
  261.   if(aPage > 1) return;
  262.  
  263.   // XXX: TODO
  264.   _logger.debug("piczoService_search: " + aQueryString);
  265.  
  266.   var aQuery = new queryHelper(aQueryString);
  267.   var myListener = {
  268.     onSuccess: function (enumResults, status) {
  269.       aListener.onSearchResult(enumResults);
  270.     },
  271.     onError: function (aFlockError) {
  272.       //_logger.info("piczoService_search ERROR: " + aFlockError.serviceErrorString + "\n");
  273.       aListener.onError(aFlockError);
  274.     }
  275.   }
  276.  
  277.   var params = Components.classes["@mozilla.org/hash-property-bag;1"]
  278.                         .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  279.   params.setPropertyAsAString("subj_id", aQuery.search);
  280.   params.setPropertyAsAString("albumlabel", aQuery.albumlabel);
  281.   if (gAPI.isLoggedIn())
  282.   {
  283.     gAPI.getPhotos(myListener, params);
  284.   } else {
  285.     _logger.info("piczoService wasnt logged in....");
  286.     // We are not logged in, force user to log in now.
  287.     var aError = Cc['@flock.com/error;1'].createInstance(Ci.flockIError);
  288.     aError.errorCode   = Ci.flockIError.PHOTOSERVICE_USER_NOT_LOGGED_IN;
  289.     aListener.onError(aError);
  290.   }
  291. }
  292.  
  293. piczoService.prototype.cancelUpload =
  294. function piczoService_cancelUpload() {
  295.   try {
  296.     _logger.info("Cancelling upload...");
  297.     gAPI.uploader.req.abort();
  298.   } catch (e) {
  299.     _logger.info("Error cancelling upload.");
  300.   }
  301. }
  302.  
  303. piczoService.prototype.upload =
  304. function piczoService_upload(aListener, aUpload, aFile) {
  305.   _logger.info("piczoService_upload");
  306.  
  307.   var params = {};
  308.   /*params.title = aUpload.title;
  309.   params.description = aUpload.description;
  310.   params.is_family = aUpload.is_family;
  311.   params.is_friend = aUpload.is_friend;
  312.   params.is_public = aUpload.is_public;
  313.   params.async = "1";
  314.   params.tags = aUpload.tags;
  315.   */
  316.   gAPI.upload(aListener, aFile, params, aUpload);
  317. }
  318.  
  319. piczoService.prototype.getAlbums = 
  320. function piczoService_getAlbums(aListener, aUserID) {
  321.   _logger.info("[piczoAPI].getAlbum call...");
  322.   var myListener = {
  323.     onResult: function (enumResults, status) {
  324.       _logger.info("[piczoAPI].getAlbums onResult!");
  325.       aListener.onGetAlbumsResult(enumResults);
  326.     },
  327.     onError: function (flockError) {
  328.       _logger.info("[piczoAPI].getAlbums onError!" + flockError.errorString);
  329.       //aListener.onError("Unable to get albums: " + flockError.errorString);
  330.     }
  331.   }
  332.  
  333.   // NOTE: the uid needs to be changed to the uid of aUsername for general user searches
  334.   gAPI.getAlbums(myListener, aUserID);
  335. }
  336.  
  337. /**
  338.  * getAlbumsForUpload
  339.  * @see flockIMediaWebService#getAlbumsForUpload
  340.  */
  341. piczoService.prototype.getAlbumsForUpload =
  342. function piczoService_getAlbumsForUpload(aListener, aUsername) {
  343.   // Needs specific implementation for uploading albums
  344.   _logger.info("[piczoAPI].getAlbumsForUpload call...");
  345.   var myListener = {
  346.     onResult: function (enumResults, status) {
  347.       _logger.info("[piczoAPI].getAlbumsForUpload onResult!");
  348.       aListener.onGetAlbumsResult(enumResults);
  349.     },
  350.     onError: function (flockError) {
  351.       _logger.info("[piczoAPI].getAlbumsForUpload onError!" + flockError.errorString);
  352.     }
  353.   }
  354.  
  355.   // NOTE: the uid needs to be changed to the uid of aUsername for general user searches
  356.   gAPI.getAlbumsForUpload(myListener, aUsername);
  357. }
  358.  
  359. piczoService.prototype.getAccountStatus = 
  360. function piczoService_getAccountStatus(aListener) {
  361.   var inst = this;
  362.   
  363.   var listener = {
  364.     onSuccess: function (aXML) {
  365.       _logger.info("piczoService: getAccountStatus Success!");
  366.       var result = Cc["@mozilla.org/hash-property-bag;1"]
  367.                    .createInstance(Ci.nsIWritablePropertyBag2);
  368.       //var username = aXML.getElementsByTagName('username')[0].firstChild.nodeValue;
  369.       //var bandwidth = aXML.getElementsByTagName('bandwidth')[0];
  370.       //var maxFileSize = aXML.getElementsByTagName('filesize')[0];
  371.       //var isPro = aXML.getElementsByTagName('user')[0].getAttribute('ispro');
  372.       result.setPropertyAsAString("maxSpace", "500000");
  373.       result.setPropertyAsAString("usedSpace", "0");
  374.       result.setPropertyAsAString("maxFileSize", "500000");
  375.       result.setPropertyAsAString("usageUnits", "bytes");
  376.       result.setPropertyAsBool("isPremium", false);
  377.       aListener.onSuccess(result, "");
  378.     },
  379.     onError: function (aError) {
  380.       aListener.onError(aError);
  381.       _logger.info("piczoService: getAccountStatus ERROR");
  382.     }
  383.   }
  384.   
  385.   gAPI.getAccountStatus(listener);
  386. }
  387.  
  388. piczoService.prototype.call =
  389. function piczoService_call(aListener, aMethod, aDictionary, aAuth)
  390. {
  391.   _logger.info("piczoService_call");
  392.   return this.doCall(aListener, aMethod, aDictionary, aAuth);
  393. }
  394.  
  395. piczoService.prototype.createAlbum =
  396. function piczoService_createAlbum(aListener, aPath)
  397. {
  398.   // XXX: TODO  
  399.   throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  400. }
  401.  
  402. piczoService.prototype.supportsSearch =
  403. function piczoService_supportsSearch(aQueryString ) {
  404.   _logger.debug("piczoService_supportsSearch: " + aQueryString);
  405.   return false;
  406. }
  407.  
  408.  
  409. // BEGIN flockIWebService interface
  410. piczoService.prototype.addAccountById =
  411. function piczoService_addAccountById(aAccountID, aIsTransient, aListener)
  412. {
  413.   _logger.info("{flockIWebService}.addAccountById('" + aAccountID + "', " + aIsTransient + ")");
  414.   var accountURN = "urn:flock:piczo:account:"+aAccountID;
  415.   var c_acct = new this._coop.Account(
  416.     accountURN, {
  417.       name: aAccountID,
  418.       accountId: aAccountID,
  419.       serviceId: CONTRACT_ID,
  420.       service: this.c_svc,
  421.       favicon: gStrings["favicon"],
  422.       URL: gStrings["userprofile"].replace("%accountid%", aAccountID),
  423.       isTransient: aIsTransient,
  424.       isPollable: true,
  425.     }
  426.   );
  427.   this._coop.accounts_root.children.add(c_acct);
  428.  
  429.   // Instanciate account component
  430.   var acct = this.getAccount(c_acct.id());
  431.   if (aListener) aListener.onSuccess(acct, "addAccount");
  432.   return acct;
  433. }
  434. // END flockIWebService interface
  435.  
  436.  
  437. piczoService.prototype.authenticateNewAccount =
  438. function flickrService_authenticateNewAccount(aListener)
  439. {
  440.   _logger.info("{flockIAuthenticateNewAccount}.authenticateNewAccount(aListener)");
  441.   var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
  442.   var win = wm.getMostRecentWindow('navigator:browser');
  443.   win.openUILinkIn(this.url, "tab");
  444. }
  445.  
  446.  
  447. // BEGIN flockIMediaWebService interface
  448. piczoService.prototype.decorateForMedia =
  449. function piczoService_decorateForMedia(aDocument)
  450. {
  451.   _logger.info("{flockIMediaWebService}.decorateForMedia()");
  452.   // XXX TODO FIXME: implement
  453. }
  454. // END flockIMediaWebService interface
  455.  
  456.  
  457. // BEGIN flockIManageableWebService interface
  458. piczoService.prototype.getAccountIDFromDocument =
  459. function piczoService_getAccountIDFromDocument(aDocument)
  460. {
  461.   _logger.info("{flockIManageableWebService}.getAccountIDFromDocument()");
  462.   aDocument.QueryInterface(Components.interfaces.nsIDOMHTMLDocument);
  463.   var acctID;
  464.   var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  465.                           .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  466.   if (this.webDetective.detect("piczo", "accountinfo", aDocument, results)) {
  467.     // If this page has the user GUID on it, then we can do a Coop lookup
  468.     // for accounts with that GUID
  469.     acctID = this._getAccountIDFromGUID(results.getPropertyAsAString("piczo_guid"));
  470.   }
  471.   if (!acctID) {
  472.     // We haven't determined the account yet.
  473.     // Since some login landing pages don't have account info on them that we
  474.     // can detect, we can try seeing if there's a session cookie stored as a
  475.     // temp password entry.  (This is a hack.)
  476.     var sessionValue = this.acUtils.getCookie("http://piczo.com",
  477.                                               gStrings["sessioncookie"]);
  478.     if (!sessionValue) {
  479.       sessionValue = this.acUtils.getCookie("http://www.piczo.com",
  480.                                             gStrings["sessioncookie"]);
  481.     }
  482.     var pw = this.acUtils.getTempPassword("piczo:session:"+sessionValue);
  483.     if (pw) {
  484.       _logger.debug("Sneaky! Got acctID from a temp password associated with session cookie!");
  485.       acctID = pw.user;
  486.     }
  487.   }
  488.   return acctID;
  489. }
  490.  
  491. piczoService.prototype.getCredentialsFromForm =
  492. function piczoService_getCredentialsFromForm(aForm)
  493. {
  494.   // Convenience method for Web Detective calls
  495.   var inst = this;
  496.   var detectForm = function piczo_detectForm(aType, aResults) {
  497.     return inst.webDetective.detectForm("piczo", aType, aForm, aResults);
  498.   };
  499.  
  500.   // Try to detect login, then signup, then changepassword, in that order
  501.   var formType = "login";
  502.   var results = getCompTK().newResults();
  503.   if (!detectForm(formType, results)) {
  504.     // No login detected, so check for signup
  505.     formType = "signup";
  506.     results = getCompTK().newResults();
  507.     if (!detectForm(formType, results)) {
  508.       // No signup detected, so try changepassword
  509.       formType = "changepassword";
  510.       results = getCompTK().newResults();
  511.       if (!detectForm(formType, results)) {
  512.         results = null;
  513.       }
  514.     }
  515.   }
  516.  
  517.   if (results) {
  518.     // We were able to get results from at least one of the checks above
  519.     var pw = {
  520.       QueryInterface: function(aIID) {
  521.         if (!aIID.equals(Components.interfaces.nsISupports) &&
  522.             !aIID.equals(Components.interfaces.nsIPassword) &&
  523.             !aIID.equals(Components.interfaces.flockIPassword))
  524.         { 
  525.           throw Components.interfaces.NS_ERROR_NO_INTERFACE;
  526.         }
  527.         return this;
  528.       },
  529.       user: results.getPropertyAsAString("username"),
  530.       password: results.getPropertyAsAString("password"),
  531.       host: null,
  532.       formType: formType
  533.     };
  534.  
  535.     // Doing a bit of a hack here...
  536.     // Since the Piczo login landing page doesn't always reveal which
  537.     // account is logged in, we will need to look at the session cookie and
  538.     // see if its the same as what it was when the user last logged in.  So
  539.     // at this point we're just storing the account username as a temp
  540.     // password entry associated with the session cookie token.
  541.     var sessionValue = this.acUtils.getCookie("http://piczo.com",
  542.                                               gStrings["sessioncookie"]);
  543.     if (!sessionValue) {
  544.       sessionValue = this.acUtils.getCookie("http://www.piczo.com",
  545.                                             gStrings["sessioncookie"]);
  546.     }
  547.     this.acUtils.clearTempPassword("piczo:session:" + sessionValue);
  548.     this.acUtils.setTempPassword("piczo:session:" + sessionValue, pw.user, "",
  549.                                  formType);
  550.     return pw;
  551.   }
  552.   return null;
  553. }
  554.  
  555. piczoService.prototype._getAccountIDFromGUID =
  556. function piczoService__getAccountIDFromGUID(aGUID)
  557. {
  558.   _logger.info("_getAccountIDFromGUID('" + aGUID + "')");
  559.   var accts = this._coop.Account.find({serviceId: CONTRACT_ID, piczo_guid: aGUID});
  560.   return (accts.length) ? accts[0].accountId : null;
  561. }
  562.  
  563. piczoService.prototype.updateAccountStatusFromDocument =
  564. function piczoService_updateAccountStatusFromDocument(aDocument)
  565. {
  566.   _logger.info("{flockIManageableWebService}.updateAccountStatusFromDocument()");
  567.   if (this.webDetective.detect("piczo", "loggedout", aDocument, null)) {
  568.     this.acUtils.markAllAccountsAsLoggedOut(CONTRACT_ID);
  569.   } else if (this.webDetective.detect("piczo", "loggedin", aDocument, null)) {
  570.     var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  571.                             .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  572.     var accountID = null;
  573.     var profileURL = null;
  574.     if (this.webDetective.detect("piczo", "accountinfo", aDocument, results)) {
  575.       var guid = results.getPropertyAsAString("piczo_guid");
  576.       accountID = this._getAccountIDFromGUID(guid);
  577.       // The profileURL is not always returned, so we need to test for it within
  578.       // a try/catch -> getPropertyAsAString will throw if it does not find the
  579.       // given property.
  580.       try {
  581.         profileURL = results.getPropertyAsAString("profileURL");
  582.       } catch (ex) {
  583.         // Didn't find profileURL. Just carry on.
  584.         _logger.debug("updateAccountStatusFromDocument: no profileURL: " + ex);
  585.       }
  586.     } else {
  587.       // Since some login landing pages don't have account info on them that we
  588.       // can detect, we can try seeing if there's a session cookie stored as a
  589.       // temp password entry.  (This is a hack.)
  590.       var sessionValue = this.acUtils.getCookie("http://piczo.com",
  591.                                                 gStrings["sessioncookie"]);
  592.       if (!sessionValue) {
  593.         sessionValue = this.acUtils.getCookie("http://www.piczo.com",
  594.                                               gStrings["sessioncookie"]);
  595.       }
  596.       var pw = this.acUtils.getTempPassword("piczo:session:"+sessionValue);
  597.       if (pw) {
  598.         _logger.debug("Sneaky! Got acctID from a temp password associated with session cookie!");
  599.         accountID = pw.user;
  600.       }
  601.     }
  602.     if (accountID && accountID.length) {
  603.       var accountURN = this.acUtils.getAccountURNById(this.urn, accountID);
  604.       if (accountURN) {
  605.         var c_acct = this._coop.get(accountURN);
  606.         var results2 = getCompTK().newResults();
  607.         if (this.webDetective.detect("piczo", "avatar", aDocument, results2)) {
  608.           c_acct.avatar = results2.getPropertyAsAString("avatarURL");
  609.         }
  610.         if (!c_acct.isAuthenticated) {
  611.           var acct = this.getAccount(accountURN);
  612.           acct.activate(null);
  613.           // Assume the API login will succeed.  The listener should handle any
  614.           // failures...
  615.           c_acct.isAuthenticated = true;
  616.         }
  617.         if (profileURL && profileURL.length) {
  618.           c_acct.URL = profileURL;
  619.         }
  620.       }
  621.     }
  622.   }
  623. }
  624. // END flockIManageableWebService interface
  625.  
  626. ///////////////////////////////////////
  627. // BEGIN flockIPollingService interface
  628. ///////////////////////////////////////
  629. piczoService.prototype.refresh =
  630. function piczoService_refresh(aURN, aPollingListener)
  631. {
  632.   _logger.info("{flockIPollingService}.refresh('" + aURN + "')");
  633.   aPollingListener.onResult();
  634. }
  635. // END flockIPollingService interface
  636.  
  637.  
  638. piczoService.prototype.init =
  639. function piczoService_init()
  640. {
  641.   var _logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  642.   _logger.init("piczoservice");
  643.  
  644.   _logger.info(".init()");
  645.  
  646.   // Prevent re-entry
  647.   if (this.mIsInitialized) return;
  648.   this.prefService = Components.classes["@mozilla.org/preferences-service;1"]
  649.                                .getService(Components.interfaces.nsIPrefBranch);
  650.   if (this.prefService.getPrefType(SERVICE_ENABLED_PREF) &&
  651.       !this.prefService.getBoolPref(SERVICE_ENABLED_PREF))
  652.   {
  653.     _logger.info("Pref " + SERVICE_ENABLED_PREF + " set to FALSE... not initializing.");
  654.     var catMgr = Cc["@mozilla.org/categorymanager;1"]
  655.       .getService(Ci.nsICategoryManager);
  656.     catMgr.deleteCategoryEntry( "wsm-startup", CATEGORY_COMPONENT_NAME, true ); 
  657.     catMgr.deleteCategoryEntry( "flockWebService", CATEGORY_ENTRY_NAME, true ); 
  658.     catMgr.deleteCategoryEntry("flockMediaProvider", CATEGORY_ENTRY_NAME, true); 
  659.     return;
  660.   }
  661.   this.mIsInitialized = true;
  662.  
  663.   // Initialize API
  664.   this.mAPI = new piczoAPI();
  665.   gAPI = this.mAPI;
  666.   gAPI.defAlbum = {};
  667.  
  668.   // Init global service handle
  669.   gSVC = this;
  670.  
  671.   // Initialize Coop, service and actions
  672.   this._coop = Components.classes["@flock.com/singleton;1"]
  673.                          .getService(Components.interfaces.flockISingleton)
  674.                          .getSingleton("chrome://flock/content/common/load-faves-coop.js")
  675.                          .wrappedJSObject;
  676.  
  677.   this.urn = "urn:piczo:service";
  678.   this.c_svc = new this._coop.Service(
  679.     this.urn, {
  680.       name: CLASS_NAME,
  681.       desc: CLASS_DESC,
  682.       serviceId: CONTRACT_ID,
  683.       contactLabel: "Friends"
  684.     }
  685.   );
  686.  
  687.   // Load Web Detective and update strings
  688.   this.webDetective = this.acUtils.useWebDetective("piczo.xml");
  689.   for (var s in gStrings) {
  690.     gStrings[s] = this.webDetective.getString("piczo", s, gStrings[s]);
  691.   }
  692.   this.url            = gStrings["mainpage"];
  693.   this.domains        = gStrings["domains"];
  694.   this.c_svc.domains  = gStrings["domains"];
  695.   this.icon           = gStrings["favicon"];
  696.   this.c_svc.loginURL = gStrings["userlogin"];
  697.  
  698.   // Update account auth states
  699.   if (this.webDetective.detectCookies("piczo", "loggedout", null)) {
  700.     this.acUtils.markAllAccountsAsLoggedOut(CONTRACT_ID);
  701.   } else {
  702.     var acctURN = this.acUtils.getFirstAuthenticatedAccountForService(CONTRACT_ID);
  703.     try {
  704.       if (acctURN) {
  705.         var acct = this.getAccount(acctURN);
  706.         acct.activate(null);
  707.       }
  708.     } catch(e) {
  709.       _logger.info(".init() ERROR: " + e);
  710.     }
  711.   }
  712. }
  713.  
  714. // ========== END piczoService class ==========
  715.  
  716. // ============================================================================================================================
  717. // ========== BEGIN piczoAPI class ============================================================================================
  718. // ============================================================================================================================
  719.  
  720. function piczoAPI()
  721. {
  722.   _logger.info("[piczoAPI] CONSTRUCTOR");
  723.   this.mAPIToken = null;
  724.   this.mAPITokenID = null;
  725.   this.mSession = {
  726.     token: null,
  727.     userGUID: null,
  728.     username: null,
  729.     password: null
  730.   };
  731.   this.mTS = 0;
  732.   this.cryptoHash = Components.classes["@mozilla.org/security/hash;1"]
  733.                               .createInstance(Components.interfaces.nsICryptoHash);
  734. }
  735.  
  736. piczoAPI.prototype.getAuthUser =
  737. function() {
  738.   return this.mSession; 
  739. }
  740.  
  741. piczoAPI.prototype.QueryInterface = function(iid) {
  742.   if (!iid.equals(Ci.nsIClassInfo) &&
  743.       !iid.equals(Ci.nsISupports)) {
  744.     throw Cr.NS_ERROR_NO_INTERFACE;
  745.   }
  746.   return this;
  747. }
  748.  
  749. piczoAPI.prototype.getTS =
  750. function piczoAPI_getTS()
  751. {
  752.   return (this.mTS++);
  753. }
  754.  
  755. piczoAPI.prototype.call =
  756. function piczoAPI_call(aMethod, aURL, aCustomHeaders, aPostVars, aListener, aSign)
  757. {
  758.   aMethod = aMethod.toUpperCase();
  759.   _logger.debug("[piczoAPI].call('" + aMethod + "', '" + aURL + "')");
  760.   var hr = Components.classes['@mozilla.org/xmlextras/xmlhttprequest;1']
  761.                      .createInstance(Components.interfaces.nsIXMLHttpRequest);
  762.   hr.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest);
  763.   hr.open(aMethod, aURL, true);
  764.   if (aMethod == "POST") {
  765.     hr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded", false);
  766.   }
  767.   // Per Bug #9373, Piczo API chokes on '+' characters in post vars, so we
  768.   // need this function to explicitly encode them.
  769.   var customEscape = function piczoAPI_customEscape(aText) {
  770.     return escape(aText).replace("+", "%2B");
  771.   };
  772.   if (aSign) {
  773.     // Need to compose message string and generate signature
  774.     var url = aURL;
  775.     var params = [];
  776.     var qmIdx = url.indexOf("?");
  777.     if (qmIdx > -1) {
  778.       var qString = url.substring(qmIdx + 1);
  779.       url = url.substring(0, qmIdx);
  780.       var qArr = qString.split("&");
  781.       for (var i = 0; i < qArr.length; i++) {
  782.         var prm = qArr[i].split("=");
  783.         params.push([prm[0], prm[1]]);
  784.       }
  785.     }
  786.     if (aCustomHeaders) {
  787.       for (var h in aCustomHeaders) {
  788.         params.push([h, aCustomHeaders[h]]);
  789.       }
  790.     }
  791.     if (aPostVars) {
  792.       for (var p in aPostVars) {
  793.         params.push([p, aPostVars[p]]);
  794.       }
  795.     }
  796.     params.sort();
  797.  
  798.     ////////////////////////////////////////////////////////
  799.     // Piczo Digital Signature format:
  800.     // ds := base-64( md5( {api-token} + {access-token} ) )
  801.     ////////////////////////////////////////////////////////
  802.     var apitoken = "";
  803.     var accesstoken = "";
  804.     for (var i = 0; i < params.length; i++) {
  805.       if(params[i][0] == "at") {
  806.         accesstoken = params[i][1];
  807.       } else if(params[i][0] == "api-token") {
  808.         apitoken = params[i][1];
  809.       }
  810.     }
  811.     var msg = apitoken + accesstoken;
  812.     var msgArr = [];
  813.     for (var i = 0; i < msg.length; i++) {
  814.       msgArr.push(msg.charCodeAt(i));
  815.     }
  816.     this.cryptoHash.init(Components.interfaces.nsICryptoHash.MD5);
  817.     this.cryptoHash.update(msgArr, msgArr.length);
  818.     var sig = this.cryptoHash.finish(true);
  819.     var dsValue = escape(sig);
  820.     dsValue = dsValue.replace(/\+/g,"%2B");
  821.     dsValue = dsValue.replace(/\//g,"%2F");
  822.     dsValue = dsValue.replace(/\@/g,"%40");
  823.     _logger.debug("message: " + msg + "  sig: " + dsValue);
  824.     hr.setRequestHeader("ds", dsValue);
  825.   }
  826.   if (aCustomHeaders) {
  827.     for (var h in aCustomHeaders) {
  828.       var val = escape(aCustomHeaders[h]);
  829.       val = val.replace(/\+/g,"%2B");
  830.       val = val.replace(/\//g,"%2F");
  831.       val = val.replace(/\@/g,"%40");
  832.       hr.setRequestHeader(h, val);
  833.     }
  834.   }
  835.   hr.onreadystatechange = function (aEvent) {
  836.     _logger.debug("[piczoAPI].call(): hr.onreadystatechange(): " + hr.readyState);
  837.     if (aListener) {
  838.       if (hr.readyState == 4) {
  839.         try {
  840.           if (hr.status/100 == 2) {
  841.             try {
  842.               aListener.onSuccess(hr, aURL);
  843.             } catch (ex) {
  844.               _logger.info(ex);
  845.               aListener.onError(hr, aURL, null);
  846.             }
  847.           } else {
  848.             // HTTP error
  849.             var err = {
  850.               errorCode: hr.status,
  851.               errorString: "HTTP error."
  852.             };
  853.             aListener.onError(hr, aURL, err);
  854.           }
  855.         } catch (ex) {
  856.           _logger.info(ex);
  857.           var err = {
  858.             errorCode: Components.interfaces.flockIError.PHOTOSERVICE_UNAVAILABLE,
  859.             errorString: "Request did not complete."
  860.           };
  861.           aListener.onError(hr, aURL, err);
  862.         }
  863.       }
  864.     }
  865.   };
  866.   var postBody = "";
  867.   if (aPostVars) {
  868.     for (var v in aPostVars) {
  869.       if (postBody.length) { postBody += "&"; }
  870.       postBody += v + "=" + customEscape(aPostVars[v]);
  871.     }
  872.   }
  873.   if ((aMethod == "POST") && postBody && postBody.length) {
  874.     hr.send(postBody);
  875.   } else {
  876.     hr.send(null);
  877.   }
  878. }
  879.  
  880. piczoAPI.prototype.getAPIToken =
  881. function piczoAPI_getAPIToken(aListener)
  882. {
  883.   _logger.debug("[piczoAPI].getAPIToken()");
  884.   var api = this;
  885.   var setupListener = {
  886.     onSuccess: function (aSubject, aTopic) {
  887.       _logger.debug("[piczoAPI].getAPIToken() setupListener.onSuccess()");
  888.       _logger.debug(aSubject.responseText);
  889.       var xmlDoc = aSubject.responseXML;
  890.       var replyEl = xmlDoc.getElementsByTagName("setup.reply").item(0);
  891.       var tokenEl = replyEl.getElementsByTagName("api-token").item(0);
  892.       var tokenIDEl = replyEl.getElementsByTagName("id").item(0);
  893.       api.mAPIToken = tokenEl.childNodes.item(0).nodeValue;
  894.       api.mAPITokenID = tokenIDEl.childNodes.item(0).nodeValue;
  895.       _logger.debug(" got API token ID:" + api.mAPITokenID);
  896.       _logger.debug(" got API token: "+api.mAPIToken);
  897.       if (aListener) { aListener.onSuccess(aSubject, aTopic); }
  898.     },
  899.     onError: function (aSubject, aTopic, aError) {
  900.       _logger.info("[piczoAPI].getAPIToken() setupListener.onError()");
  901.       if (aError) {
  902.         for (var p in aError) {
  903.           _logger.info("error[" + p + "] = " + aError[p]);
  904.         }
  905.       }
  906.       for (var p in aSubject) {
  907.         _logger.debug("response[" + p + "] = " + aSubject[p]);
  908.       }
  909.       if (aListener) { aListener.onError(aSubject, aTopic, aError); }
  910.     }
  911.   };
  912.   var url = gStrings["api-setup-URL"].replace("%agent%", gStrings["api-agent-string"]);
  913.   var postVars = {
  914.     "api-agent": gStrings["api-agent-string"]
  915.   };
  916.   this.call("post", url, null, postVars, setupListener);
  917. }
  918.  
  919. piczoAPI.prototype.getPhotosCall =
  920. function piczoService_authenticatedGetPhotosCall(aListener, aMethod, aDictionary)
  921. {
  922.   var api = this;
  923.   api.pageID = null;
  924.   api.pageToFind = aDictionary.albumlabel;
  925.  
  926.   _logger.debug("piczoService_authenticatedCall " + aMethod);
  927.   if (!this.isLoggedIn) {
  928.     _logger.debug("not logged in, so trying unauth call for " + aMethod);
  929.     return this.call(aListener, aMethod, aDictionary);
  930.   }
  931.   
  932.   var pageToFind = aDictionary.albumlabel;
  933.  
  934.   var callGetPhotos = function () {
  935.     var url = "http://api.piczo.com/api-1/lookup";
  936.  
  937.     if (api.pageID) {
  938.       url += "/" + api.pageID; 
  939.     }
  940.  
  941.     url += "/images?reply=rss";
  942.  
  943.     var headers = {
  944.       "api-token": this.gAPI.mAPIToken,
  945.       "at": this.gAPI.mSession.token
  946.     };
  947.     api.call("get", url, headers, null, aListener, true);
  948.   };
  949.  
  950.   var pagesListener = {
  951.     onSuccess: function pagesListener_onSuccess(aSubject, aTopic) {
  952.       var xml = aSubject.responseXML;
  953.       _logger.debug("[piczoAPI].getPages returned: "
  954.                     + aSubject.responseText);
  955.  
  956.       var items = xml.getElementsByTagName("item");
  957.  
  958.       for (var i = 0; i < items.length; i++) {
  959.         var guid = items[i].getElementsByTagName("guid")[0]
  960.                    .firstChild.nodeValue;
  961.         var pagelabel = items[i].getElementsByTagName("description")[0]
  962.                         .firstChild.nodeValue;
  963.         
  964.         if (api.pageToFind == pagelabel) {
  965.           api.pageID = guid;
  966.         }
  967.       }
  968.  
  969.       callGetPhotos();
  970.       return;
  971.     },
  972.     onError: function pagesListener_onError(aSubject, aTopic, aFlockError) {
  973.       _logger.error("Error getting pages" + aFlockError.errorString);
  974.       if (api.handleStaleToken(aSubject)) {
  975.         // Resubmit the request with the new token
  976.         api.getPhotosCall(aListener, "piczo.photos.get", aDictionary);
  977.       } else {
  978.         aListener.onError(aFlockError);
  979.       }
  980.     } 
  981.   };
  982.  
  983.   // Get Page ID first
  984.   var callGetPages = function callGetPages_function() {
  985.     var url = "http://api.piczo.com/api-1/lookup/pages?reply=rss";
  986.  
  987.     var headers = {
  988.       "api-token": this.gAPI.mAPIToken,
  989.       "at": this.gAPI.mSession.token
  990.     };
  991.     api.call("get", url, headers, null, pagesListener, true);
  992.   }
  993.  
  994.   if (this.mAPIToken) {
  995.     _logger.info("piczoService Calling getphotos");
  996.     if (pageToFind != null) {
  997.       callGetPages();
  998.     } else {
  999.       callGetPhotos();
  1000.     }
  1001.   } else {
  1002.     _logger.info("piczoService wasnt logged in....");
  1003.     // We are not logged in, force user to log in now.
  1004.     var aError = Cc['@flock.com/error;1'].createInstance(Ci.flockIError);
  1005.     aError.errorCode   = Ci.flockIError.PHOTOSERVICE_USER_NOT_LOGGED_IN;
  1006.     aListener.onError(aError);
  1007.   } 
  1008. }
  1009.  
  1010. piczoAPI.prototype.getAccountStatus = 
  1011. function piczoAPI_getAccountStatus(aListener) {
  1012.   _logger.info("[piczoAPI].getAccountStatus");
  1013. }
  1014.  
  1015. /**
  1016.  * getAlbums
  1017.  * @see flockIMediaWebService#getAlbums
  1018.  */
  1019. piczoAPI.prototype.getAlbums = 
  1020. function piczoAPI_getAlbums(aListener, aParams) {
  1021.   _logger.info("[piczoAPI].getAlbums('" + aParams + "')");
  1022.  
  1023.   var api = this;
  1024.   var pagesListener = {
  1025.     onSuccess: function pagesListener_onSuccess(aSubject, aTopic) {
  1026.       var xml = aSubject.responseXML;
  1027.       _logger.debug("[piczoAPI].getPages returned: "
  1028.                     + aSubject.responseText);
  1029.  
  1030.       var photoAlbums = [];
  1031.  
  1032.       var items = xml.getElementsByTagName("item");
  1033.  
  1034.       for (var i = 0; i < items.length; i++) {
  1035.         var guid = items[i].getElementsByTagName("guid")[0]
  1036.                            .firstChild.nodeValue;
  1037.         var pagelabel = items[i].getElementsByTagName("description")[0]
  1038.                                 .firstChild.nodeValue;
  1039.         var newAlbum = Cc[FLOCK_PHOTO_ALBUM_CONTRACTID]
  1040.                                  .createInstance(Ci.flockIPhotoAlbum);
  1041.  
  1042.         newAlbum.id = guid;
  1043.         newAlbum.title = pagelabel;
  1044.  
  1045.         if (!gAPI.defAlbum.id) {
  1046.           gAPI.defAlbum = newAlbum;
  1047.         }
  1048.  
  1049.         photoAlbums.push(newAlbum);
  1050.       }
  1051.  
  1052.       var albumsEnum = {
  1053.         hasMoreElements: function() {
  1054.           return (photoAlbums.length > 0);
  1055.         },
  1056.         getNext: function() {
  1057.           return photoAlbums.shift();
  1058.         }
  1059.       }
  1060.  
  1061.       aListener.onResult(albumsEnum, null);
  1062.     },
  1063.     onError: function pagesListener_onError(aSubject, aTopic, aFlockError) {
  1064.       _logger.error("Error getting pages" + aFlockError.errorString);
  1065.       if (api.handleStaleToken(aSubject)) {
  1066.         // Resubmit the request with the new token
  1067.         api.getAlbums(aListener, "piczo.photos.get", aDictionary);
  1068.       } else {
  1069.         aListener.onError(aFlockError);
  1070.       }
  1071.     } 
  1072.   };
  1073.  
  1074.   // Get Page ID first
  1075.   var callGetPages = function callGetPages_function() {
  1076.     var url = "http://api.piczo.com/api-1/lookup/pages?reply=rss";
  1077.  
  1078.     var headers = {
  1079.       "api-token": this.gAPI.mAPIToken,
  1080.       "at": this.gAPI.mSession.token
  1081.     };
  1082.     api.call("get", url, headers, null, pagesListener, true);
  1083.   }
  1084.  
  1085.   if (this.mAPIToken) {
  1086.     _logger.debug("piczoService Calling getInbox");
  1087.     callGetPages();
  1088.   } else {
  1089.     _logger.debug("piczoService wasnt logged in....");
  1090.     // We are not logged in, force user to log in now.
  1091.     var aError = Cc['@flock.com/error;1'].createInstance(Ci.flockIError);
  1092.     aError.errorCode   = Ci.flockIError.PHOTOSERVICE_USER_NOT_LOGGED_IN;
  1093.     aListener.onError(aError);
  1094.   }
  1095. }
  1096.  
  1097. /**
  1098.  * getAlbumsForUpload
  1099.  * @see flockIMediaWebService#getAlbumsForUpload
  1100.  */
  1101. piczoAPI.prototype.getAlbumsForUpload = 
  1102. function piczoAPI_getAlbumsForUpload(aListener, aParams) {
  1103.   _logger.debug("[piczoAPI].getAlbumsForUpload('" + aParams + "')");
  1104.  
  1105.   var api = this;
  1106.  
  1107.   // Make inbox call
  1108.   //
  1109.   var listener = {
  1110.     onSuccess: function(aSubject, aTopic) {
  1111.       //try {
  1112.         var xml          = aSubject.responseXML;
  1113.         _logger.debug("[piczoAPI].getInbox returned: " + aSubject.responseText);
  1114.         var destinations = xml.getElementsByTagName("destination");
  1115.  
  1116.         var photoAlbums = [];
  1117.         _logger.debug("[piczoAPI].getInbox parsing data: " + destinations.length);
  1118.  
  1119.         gAPI.defAlbum = {};
  1120.  
  1121.         for (var i = 0; i < destinations.length; i++) {
  1122.           var name = destinations[i].getElementsByTagName('name')[0].firstChild.nodeValue;
  1123.           var uri = destinations[i].getElementsByTagName('action-uri')[0].firstChild.nodeValue;
  1124.  
  1125.           _logger.debug("[piczoAPI].getInbox data: " + name + ", " + uri);
  1126.  
  1127.           var newAlbum = Components.classes[FLOCK_PHOTO_ALBUM_CONTRACTID]
  1128.                                    .createInstance(Ci.flockIPhotoAlbum);
  1129.           newAlbum.id = uri;
  1130.           newAlbum.title = name;
  1131.  
  1132.           if(!gAPI.defAlbum.id) {
  1133.             gAPI.defAlbum = newAlbum;
  1134.           }
  1135.  
  1136.           photoAlbums.push(newAlbum);
  1137.         }
  1138.  
  1139.         var enum_ = {
  1140.           hasMoreElements: function() {
  1141.             return (photoAlbums.length > 0);
  1142.           },
  1143.           getNext: function() {
  1144.             return photoAlbums.shift();
  1145.           }
  1146.         }
  1147.  
  1148.         _logger.debug("[piczoAPI].getInbox returning results...");
  1149.         aListener.onResult(enum_, null);
  1150.     },
  1151.     onError: function(aSubject, aTopic, aFlockError) {
  1152.       _logger.info('Error getting inbox' + aFlockError.errorString);
  1153.       if (api.handleStaleToken(aSubject)) {
  1154.         // Resubmit the request with the new token
  1155.         api.getAlbumsForUpload(aListener, aParams);
  1156.       } else {
  1157.         aListener.onError(aFlockError);
  1158.       }
  1159.     }
  1160.   };
  1161.  
  1162.   var callGetInbox = function () {
  1163.     var url = "http://api.piczo.com/api-1/inbox" + "/images";
  1164.  
  1165.     var headers = {
  1166.       "api-token": this.gAPI.mAPIToken,
  1167.       "at": this.gAPI.mSession.token
  1168.     };
  1169.     api.call("get", url, headers, null, listener, true);
  1170.   };
  1171.  
  1172.  
  1173.   if (this.mAPIToken) {
  1174.     _logger.info("piczoService Calling getInbox");
  1175.     callGetInbox();
  1176.   } else {
  1177.     _logger.info("piczoService wasnt logged in....");
  1178.     // We are not logged in, force user to log in now.
  1179.     var aError = Cc['@flock.com/error;1'].createInstance(Ci.flockIError);
  1180.     aError.errorCode   = Ci.flockIError.PHOTOSERVICE_USER_NOT_LOGGED_IN;
  1181.     aListener.onError(aError);
  1182.     return;
  1183.   }
  1184. }
  1185.  
  1186. // Handle the case where the session token goes stale
  1187. // If stale reset and return true, otherwise return false
  1188. piczoAPI.prototype.handleStaleToken =
  1189. function(aSubject) {
  1190.   
  1191.   // When a session token goes stale the piczo api  
  1192.   // marks the response with an http error (401 - unauthorized)
  1193.   // and includes error information with a new session token 
  1194.   
  1195.   _logger.debug("handleStaleToken()");
  1196.   var api = this;
  1197.   if (aSubject.status == 401) {
  1198.     var xml = aSubject.responseXML;
  1199.     _logger.debug(aSubject.responseText);
  1200.     
  1201.     var errorEl = xml.getElementsByTagName("errors").item(0);
  1202.     if (!errorEl || (errorEl.length == 0)) {
  1203.       return false;
  1204.     }
  1205.  
  1206.     var invalidCrdntlEl = errorEl.getElementsByTagName("error.invalid-credential").item(0);
  1207.     if (!invalidCrdntlEl || (invalidCrdntlEl.length == 0)) {
  1208.       return false;
  1209.     }
  1210.  
  1211.     var arglinkEl = invalidCrdntlEl.getElementsByTagName("argument").item(0);
  1212.     if (!arglinkEl || (arglinkEl.length == 0)) {
  1213.       return false;
  1214.     }
  1215.  
  1216.     var problem = arglinkEl.getAttribute("problem");
  1217.     if (!problem) {
  1218.       return false;
  1219.     }
  1220.  
  1221.     _logger.debug("problem = " + problem);
  1222.     if (problem == "expired-access") { 
  1223.       // Our access token went stale so grab the new one from the response
  1224.       var sessiontokenEl = errorEl.getElementsByTagName("at").item(0);
  1225.       var sessiontoken = sessiontokenEl.childNodes.item(0).nodeValue;
  1226.       _logger.debug("new access token = " + sessiontoken);
  1227.       api.mSession.token = sessiontoken;
  1228.       return true;
  1229.     }
  1230.   }
  1231.   return false; 
  1232. }
  1233.  
  1234. piczoAPI.prototype.getPhotos = 
  1235. function piczoAPI_getPhotos(aListener, aParams) {
  1236.   _logger.debug("[piczoAPI].getPhotos('" + aParams + "')");
  1237.   var api = this;
  1238.   var listener = {
  1239.     onSuccess: function(aSubject, aTopic) {
  1240.       var photos = [];
  1241.       _logger.debug("[piczoAPI].getPhotos returned: " + aSubject.responseText);
  1242.       var xml = aSubject.responseXML;
  1243.  
  1244.       var photoList = xml.getElementsByTagName("item");
  1245.       for (var i = 0; i < photoList.length; i++) {
  1246.         var photo = photoList[i];
  1247.  
  1248.         var id = photo.getElementsByTagName('guid')[0].firstChild.nodeValue;
  1249.         var link = photo.getElementsByTagName('link')[0].firstChild.nodeValue;
  1250.         var description = photo.getElementsByTagName('description')[0].firstChild.nodeValue;
  1251.         var thumbnail = photo.getElementsByTagName("thumbnail")
  1252.                              .item(0)
  1253.                              .getAttribute("url");
  1254.  
  1255.         var newPhoto = new piczoPhoto();
  1256.         newPhoto.webPageUrl = link;
  1257.         newPhoto.thumbnail = link;
  1258.         newPhoto.midSizePhoto = thumbnail;
  1259.         newPhoto.largeSizePhoto = link;
  1260.         newPhoto.title = description;
  1261.         newPhoto.id = id;
  1262.         newPhoto.is_public = true;
  1263.         newPhoto.is_video = false;
  1264.  
  1265.         photos.unshift(newPhoto);
  1266.       }
  1267.  
  1268.       aListener.onSuccess(createEnum(photos), 'success');
  1269.     },
  1270.     onError: function(aSubject, aTopic, aFlockError) {
  1271.       if(aFlockError) {
  1272.         _logger.info('Error getting photos' + aFlockError.errorString);
  1273.       } else {
  1274.         _logger.info("Error getting photos");
  1275.       }
  1276.  
  1277.       if (api.handleStaleToken(aSubject)) {
  1278.         // Resubmit the request with the new token
  1279.         api.getPhotos(aListener,aParams);
  1280.       } else {
  1281.         aListener.onError(aFlockError);
  1282.       }
  1283.     }
  1284.   };
  1285.  
  1286.   var params = {
  1287.     subj_id: aParams.getPropertyAsAString("subj_id"),
  1288.     albumlabel: aParams.getPropertyAsAString("albumlabel")
  1289.   };
  1290.  
  1291.   _logger.info("calling getphotoscall"); 
  1292.   api.getPhotosCall(listener, 'piczo.photos.get', params);
  1293. }
  1294.  
  1295. piczoAPI.prototype.login =
  1296. function piczoAPI_login(aUsername, aPassword, aListener)
  1297. {
  1298.   _logger.debug("[piczoAPI].login('" + aUsername + "')");
  1299.   var api = this;
  1300.   if (aUsername) { this.mSession.username = aUsername; }
  1301.   if (aPassword) { this.mSession.password = aPassword; }
  1302.  
  1303.   var loginListener = {
  1304.     onSuccess: function (aSubject, aTopic) {
  1305.       _logger.info("[piczoAPI].login() loginListener.onSuccess()");
  1306.       _logger.debug(aSubject.responseText);
  1307.       var xmlDoc = aSubject.responseXML;
  1308.       var replyEl = xmlDoc.getElementsByTagName("login.reply").item(0);
  1309.       var userlinkEl = replyEl.getElementsByTagName("user.id").item(0);
  1310.       api.mSession.userGUID = userlinkEl.getAttribute("guid");
  1311.       _logger.debug(" got user GUID: "+api.mSession.userGUID);
  1312.       var sessiontokenEl = replyEl.getElementsByTagName("at").item(0);
  1313.       api.mSession.token = sessiontokenEl.childNodes.item(0).nodeValue;
  1314.       _logger.debug(" got session token: "+api.mSession.token);
  1315.   
  1316.       if (aListener) { aListener.onSuccess(aSubject, aTopic); }
  1317.     },
  1318.     onError: function (aSubject, aTopic, aError) {
  1319.       _logger.info("[piczoAPI].login() loginListener.onError()");
  1320.       if (aListener) { aListener.onError(aSubject, aTopic, aError); }
  1321.     }
  1322.   };
  1323.  
  1324.   var callLogin = function () {
  1325.     var url = gStrings["api-login-URL"];
  1326.     var headers = {
  1327.       "ts": api.getTS()
  1328.     };
  1329.     var postVars = {
  1330.       "user-name": api.mSession.username,
  1331.       "user-password": api.mSession.password,
  1332.       "id": api.mAPITokenID,
  1333.       "api-token": api.mAPIToken
  1334.     };
  1335.     api.call("post", url, headers, postVars, loginListener);
  1336.   };
  1337.  
  1338.   if (this.mAPIToken) {
  1339.     callLogin();
  1340.   } else {
  1341.     // Need to get an API token before we can log in
  1342.     var tokenListener = {
  1343.       onSuccess: function (aSubject, aTopic) {
  1344.         callLogin();
  1345.       },
  1346.       onError: function (aSubject, aTopic, aError) {
  1347.         if (aListener) { aListener.onError(aSubject, aTopic, aError); }
  1348.       }
  1349.     };
  1350.     this.getAPIToken(tokenListener);
  1351.   }
  1352. }
  1353.  
  1354. piczoAPI.prototype.logout =
  1355. function piczoAPI_logout()
  1356. {
  1357.   _logger.info("[piczoAPI].logout()");
  1358.   this.mSession = [];
  1359.   
  1360.   var api = this;
  1361.  
  1362.   var logoutListener = {
  1363.     onSuccess: function (aSubject, aTopic) {
  1364.       _logger.info("[piczoAPI].logout() logoutListener.onSuccess()");
  1365.       _logger.debug(aSubject.responseText);
  1366.       var xmlDoc = aSubject.responseXML;
  1367.       var replyEl = xmlDoc.getElementsByTagName("logout.reply").item(0);
  1368.     },
  1369.     onError: function (aSubject, aTopic, aError) {
  1370.       _logger.info("[piczoAPI].logout() logoutListener.onError()");
  1371.     }
  1372.   };
  1373.  
  1374.   var callLogout = function () {
  1375.     var url = gStrings["api-logout-URL"];
  1376.     var headers = {
  1377.       "ts": api.getTS()
  1378.     };
  1379.     var postVars = {
  1380.       "user-name": api.mSession.username,
  1381.       "user-password": api.mSession.password,
  1382.       "id": api.mAPITokenID,
  1383.       "api-token": api.mAPIToken
  1384.     };
  1385.     api.call("post", url, headers, postVars, logoutListener);
  1386.   };
  1387.  
  1388.   if (this.mAPIToken) {
  1389.     callLogout();
  1390.   } else {
  1391.     // Need to get an API token before we can log out
  1392.     var tokenListener = {
  1393.       onSuccess: function (aSubject, aTopic) {
  1394.         callLogout();
  1395.       },
  1396.       onError: function (aSubject, aTopic, aError) {
  1397.         if (aListener) { aListener.onError(aSubject, aTopic, aError); }
  1398.       }
  1399.     };
  1400.     this.getAPIToken(tokenListener);
  1401.   }  
  1402. }
  1403.  
  1404. piczoAPI.prototype.upload = 
  1405. function piczoAPI_upload(aListener, aPhoto, aParams, aUpload) {
  1406.   _logger.info("piczoAPI_upload");
  1407.   // XXX: TODO
  1408.   var inst = this;
  1409.   this.uploader = new PhotoUploader();
  1410.   var myListener = {
  1411.     onResult: function(aXML) {
  1412.       _logger.debug("piczoAPI_upload: Got a result back from uploader: " + aXML.responseText);
  1413.       aListener.onUploadComplete(aUpload);
  1414.     },
  1415.     onError: function(aErrorCode) {
  1416.       _logger.info("piczoAPI_upload: " + aErrorCode);
  1417.       aListener.onError(aErrorCode);
  1418.     },
  1419.     onProgress: function(aCurrentProgress) {
  1420.       _logger.info("piczoAPI_upload: Progress");
  1421.       aListener.onProgress(aCurrentProgress);
  1422.     }
  1423.   }
  1424.   gAPI.convertBools(aParams);
  1425.   gAPI.convertTags(aParams);
  1426.  
  1427.   var dsvalue = this.getDSValue();
  1428.  
  1429.   // Check for default album seleciton
  1430.   if(!aUpload.album) {
  1431.     // auto-select the first album
  1432.     aUpload.album = gAPI.defAlbum.id;
  1433.   }
  1434.  
  1435.   this.uploader.setEndpoint(aUpload.album + "&ds=" + dsvalue);
  1436.   this.uploader.upload(myListener, aPhoto, aParams);
  1437. }
  1438.  
  1439. piczoAPI.prototype.getDSValue = 
  1440. function piczAPI_getDSValue() {
  1441.   _logger.info("piczoAPI_getDSValue");
  1442.   var msg = gAPI.mAPIToken + gAPI.mSession.token;
  1443.   var msgArr = [];
  1444.   for (var i = 0; i < msg.length; i++) {
  1445.     msgArr.push(msg.charCodeAt(i));
  1446.   }
  1447.   this.cryptoHash.init(Components.interfaces.nsICryptoHash.MD5);
  1448.   this.cryptoHash.update(msgArr, msgArr.length);
  1449.   var sig = this.cryptoHash.finish(true);
  1450.   var dsValue = escape(sig);
  1451.   dsValue = dsValue.replace(/\+/g,"%2B");
  1452.   dsValue = dsValue.replace(/\//g,"%2F");
  1453.   dsValue = dsValue.replace(/\@/g,"%40");
  1454.  
  1455.   return dsValue;
  1456. }
  1457.  
  1458. piczoAPI.prototype.convertBools = 
  1459. function piczoAPI_convertBools(aParams) {
  1460.   for (var p in aParams) {
  1461.     if (!p.match(/^is/)) continue;
  1462.     // I hope that this doesn't break anything
  1463.     if (aParams[p]=="true") aParams[p] = "1";
  1464.     if (aParams[p]=="false") aParams[p] = "0";
  1465.   }
  1466. }
  1467.  
  1468. piczoAPI.prototype.convertTags = 
  1469. function piczoAPI_convertTags(aParams) {
  1470.   for (var p in aParams) {
  1471.     if (p != "tags") continue;
  1472.     var tags = aParams[p].split(",");
  1473.     for (var i = 0; i < tags.length;++i) {
  1474.       tags[i] = '"' + tags[i] + '"';
  1475.       tags[i] = tags[i].replace(/\"+/g,'"');
  1476.     }
  1477.     aParams[p] = tags.join(",");
  1478.   }
  1479. }
  1480.  
  1481. piczoAPI.prototype.isLoggedIn =
  1482. function piczoAPI_isLoggedIn()
  1483. {
  1484.   return (this.mSession.token && this.mSession.token.length);
  1485. }
  1486.  
  1487. piczoAPI.prototype.handleDragDrop = 
  1488. function piczoAPI_handleDragDrop(url, locX, locY) {
  1489.   _logger.info("piczoAPI_handleDragDrop: " + url + ", " + locX + ", " + locY);
  1490.   // http://server/go/placeimage
  1491.   // parameters:
  1492.   // imgid  (Image Id)
  1493.   // pageid (Destination page id)
  1494.   // sl     (x position)
  1495.   // st     (y position)
  1496.  
  1497.   // Make sure we are logged in
  1498.   // Hack it up
  1499.   var imageID;
  1500.   var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  1501.                           .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  1502.   if (gSVC.webDetective.detectNoDOM("piczo", "piczoImageID", null, url, results)) {
  1503.     imageID = results.getPropertyAsAString("imageID");
  1504.   }
  1505.  
  1506.   var currTab;
  1507.   // Get active tab
  1508.   var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
  1509.   var win = wm.getMostRecentWindow('navigator:browser');
  1510.   if (win) {
  1511.     var gBrowser = win.gBrowser;
  1512.  
  1513.     for(var i=0;i<gBrowser.mTabs.length;i++) {
  1514.       var browser = gBrowser.mTabs[i].linkedBrowser;
  1515.  
  1516.       if(browser.currentURI.spec == gBrowser.currentURI.spec) {
  1517.         // We found the activce tab
  1518.         currTab = browser;
  1519.       }
  1520.     }
  1521.   }
  1522.  
  1523.   // Get active tab's top-level document
  1524.   _logger.debug("piczoAPI: document: " + currTab.contentDocument); 
  1525.   var theDoc = currTab.contentDocument;
  1526.  
  1527.   // Make sure we're in a piczo edit page
  1528.   results = Components.classes["@mozilla.org/hash-property-bag;1"]
  1529.                       .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  1530.   if (!gSVC.webDetective.detect("piczo", "piczoedit", theDoc, results)) {
  1531.     // not a piczo edit page, return.
  1532.     _logger.info("[piczoAPI] handleDragDrop() - Not a piczo edit page");
  1533.     return false;
  1534.   }
  1535.  
  1536.   const PAGEID_STR = "pageid";
  1537.   var serverLoc;
  1538.   var loc = currTab.currentURI.spec;
  1539.   if(loc.match(/:\/\/([^\/]*)\//)) {
  1540.     serverLoc = RegExp.$1;
  1541.   }
  1542.   
  1543.   _logger.debug("piczoAPI: ServerLoc = " + serverLoc);
  1544.  
  1545.   // Get array of top-level documents' immediate children documents (sub-frames)
  1546.   var frames = theDoc.getElementsByTagName('frame');
  1547.   var iframes = theDoc.getElementsByTagName('iframe');
  1548.  
  1549.   // apply webdetective rule to each document and grab pageid
  1550.   var frameFound = null;
  1551.   var pageID = null;
  1552.   results = Components.classes["@mozilla.org/hash-property-bag;1"]
  1553.                           .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  1554.   for (var i = 0; i < frames.length; i++) {
  1555.     if (gSVC.webDetective.detect("piczo", "webedit", frames[i].contentDocument, results)) {
  1556.       pageID = results.getPropertyAsAString(PAGEID_STR);
  1557.       frameFound = frames[i];
  1558.       break;
  1559.     }
  1560.   }
  1561.   for (var i = 0; i < iframes.length && !frameFound; i++) {
  1562.     if (gSVC.webDetective.detect("piczo", "webedit", iframes[i].contentDocument, results)) {
  1563.       pageID = results.getPropertyAsAString(PAGEID_STR);
  1564.       frameFound = iframes[i];
  1565.       break;
  1566.     }
  1567.   }
  1568.   _logger.debug("piczoAPI: pageid=" + pageID);
  1569.  
  1570.   // Setup the url 
  1571.   var aURL = "http://" + serverLoc
  1572.               + "/go/placeimage?imgid=" + imageID
  1573.               + "&pageid=" + pageID
  1574.               + "&sl=" + locX 
  1575.               + "&st=" + locY;
  1576.  
  1577.   // Handler for response.
  1578.   var listener = {
  1579.     onSuccess: function (aSubject, aTopic) {
  1580.       _logger.info("[piczoAPI].handleDragDrop() handleDragDropListener.onSuccess()");
  1581.       _logger.debug("page frame before string mangling: " + frameFound.src);
  1582.  
  1583.       // need to do a little string mangling to fool the service to reload a
  1584.       // child frame instead of the parent page
  1585.       // pass in the child frame pageid and reload
  1586.       var re = /pageid=\d+/;
  1587.       frameFound.src = frameFound.src.replace(re, PAGEID_STR + "=" + pageID);
  1588.  
  1589.       _logger.debug("page frame after string mangling: " + frameFound.src);
  1590.       return true;
  1591.     },
  1592.     onError: function (flockError) {
  1593.       return false;
  1594.     }
  1595.   }
  1596.  
  1597.   // Make simple xmlhttprequest to "move" the image to the right location
  1598.   var hr = Components.classes['@mozilla.org/xmlextras/xmlhttprequest;1']
  1599.                      .createInstance(Components.interfaces.nsIXMLHttpRequest);
  1600.   hr.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest);
  1601.   hr.onreadystatechange = function (aEvent) {
  1602.     _logger.debug("[piczoAPI].call(): hr.onreadystatechange for Moving URL: "+hr.readyState);
  1603.     if (listener) {
  1604.       if (hr.readyState == 4) {
  1605.         try {
  1606.           if (hr.status/100 == 2) {
  1607.             try {
  1608.               listener.onSuccess(hr, aURL);
  1609.             } catch (ex) {
  1610.               _logger.debug(ex);
  1611.               listener.onError(null);
  1612.             }
  1613.           } else {
  1614.             // HTTP error
  1615.             listener.onError(null);
  1616.           }
  1617.         } catch (ex) {
  1618.           _logger.info(ex);
  1619.           var err = {
  1620.             errorCode: Components.interfaces.flockIError.PHOTOSERVICE_UNAVAILABLE,
  1621.             errorString: "Request did not complete."
  1622.           };
  1623.           listener.onError(err);
  1624.         }
  1625.       }
  1626.     }
  1627.   };
  1628.   hr.open("GET", aURL, true);
  1629.   hr.send(null);
  1630.  
  1631.   // We are returning true at this point because we made it to the send() call,
  1632.   // but we won't know if the send actually succeeded because it is being called
  1633.   // asynchronously (and cannot be called synchronously, as we need to use the
  1634.   // callbacks).
  1635.   return true;
  1636. }
  1637.  
  1638. piczoAPI.prototype.getFriends =
  1639. function piczoAPI_getFriends(aListener)
  1640. {
  1641.   _logger.info("[piczoAPI].getFriends()");
  1642.   var api = this;
  1643.  
  1644.   var getNodeSubvalue = function (aNode) {
  1645.     var longest = "";
  1646.     for (var i = 0; i < aNode.childNodes.length; i++) {
  1647.       var item = aNode.childNodes.item(i);
  1648.       if (item.nodeValue.length > longest.length) {
  1649.         longest = item.nodeValue;
  1650.       }
  1651.     }
  1652.     return longest;
  1653.   };
  1654.  
  1655.   var friendsListener = {
  1656.     onSuccess: function (aSubject, aTopic) {
  1657.       _logger.info("[piczoAPI].getFriends() friendsListener.onSuccess()");
  1658.       _logger.debug(aSubject.responseText);
  1659.       var replyEl = aSubject.responseXML.getElementsByTagName("lookup.reply").item(0);
  1660.       var friendsEl = replyEl.getElementsByTagName("friends").item(0);
  1661.       var friendEls = friendsEl.getElementsByTagName("friend");
  1662.       var friends = [];
  1663.       for (var i = 0; i < friendEls.length; i++) {
  1664.         var friendEl = friendEls.item(i);
  1665.         var primaryProfEl = friendEl.getElementsByTagName("primary-profile").item(0);
  1666.         var thumbEl = primaryProfEl.getElementsByTagName("thumbnail").item(0);
  1667.         var websiteEl = primaryProfEl.getElementsByTagName("website-link").item(0);
  1668.         friends.push({
  1669.           id: friendEl.getAttribute("guid"),
  1670.           name: getNodeSubvalue(friendEl.getElementsByTagName("name").item(0)),
  1671.           avatarURL: getNodeSubvalue(thumbEl.getElementsByTagName("uri").item(0)),
  1672.           URL: getNodeSubvalue(websiteEl.getElementsByTagName("uri").item(0))
  1673.         });
  1674.       }
  1675.       aListener.onSuccess(friends, aTopic);
  1676.     },
  1677.     onError: function (aSubject, aTopic, aFlockError) {
  1678.       _logger.info("[piczoAPI].getFriends() friendsListener.onError()");
  1679.       if (api.handleStaleToken(aSubject)) {
  1680.         // Resubmit the request with the new token
  1681.         callGetFriends();
  1682.       } else {
  1683.         aListener.onError(aFlockError);
  1684.       }
  1685.     }
  1686.   };
  1687.  
  1688.   var callGetFriends = function () {
  1689.     var url = gStrings["api-friends-URL"];
  1690.     var headers = {
  1691.       "ts": api.getTS(),
  1692.       "sn": api.mSession.token,
  1693.     };
  1694.     api.call("get", url, headers, null, friendsListener, true);
  1695.   };
  1696.  
  1697.   callGetFriends();
  1698. }
  1699.  
  1700. // ========== END piczoAPI class ==========
  1701.  
  1702.  
  1703.  
  1704. // ==============================================
  1705. // ========== BEGIN piczoAccount class ==========
  1706. // ==============================================
  1707.  
  1708. function piczoAccount()
  1709. {
  1710.   _logger.info("piczoAccount CONSTRUCTOR");
  1711.   this.acUtils = Components.classes["@flock.com/account-utils;1"]
  1712.                            .getService(Components.interfaces.flockIAccountUtils);
  1713.   this._coop = Components.classes["@flock.com/singleton;1"]
  1714.                          .getService(Components.interfaces.flockISingleton)
  1715.                          .getSingleton("chrome://flock/content/common/load-faves-coop.js")
  1716.                          .wrappedJSObject;
  1717.   this.mAPI = gAPI;
  1718.   this._ctk = {
  1719.     interfaces: [
  1720.       "nsISupports",
  1721.       "flockIWebServiceAccount",
  1722.       "flockIMediaWebServiceAccount",
  1723.       "flockIMediaUploadAccount",
  1724.     ],
  1725.   };
  1726.   getCompTK().addAllInterfaces(this);
  1727.  
  1728.   _logger.debug("piczoAccount Constructor End.");
  1729. }
  1730.  
  1731. piczoAccount.prototype.urn = null;
  1732.  
  1733. // BEGIN flockIWebServiceAccount interface
  1734. piczoAccount.prototype.login =
  1735. function piczoAccount_login(aListener)
  1736. {
  1737.   var account = this;
  1738.   var c_acct = account._coop.get(account.urn);
  1739.   var loginListener = {
  1740.     onSuccess: function (aSubject, aTopic) {
  1741.       account.acUtils.ensureOnlyAuthenticatedAccount(account.urn);
  1742.       c_acct.piczo_guid = account.mAPI.mSession.userGUID;
  1743.       if (aListener) { aListener.onSuccess(aSubject, aTopic); }
  1744.     },
  1745.     onError: function (aSubject, aTopic, aError) {
  1746.       c_acct.isAuthenticated = false;
  1747.       if (aListener) { aListener.onError(aSubject, aTopic, aError); }
  1748.     }
  1749.   };
  1750.   var pw = this.acUtils.getPassword("urn:piczo:service"+':'+c_acct.accountId);
  1751.   this.mAPI.login(pw.user, pw.password, loginListener);
  1752. }
  1753.  
  1754. piczoAccount.prototype.logout =
  1755. function piczoAccount_logout(aListener)
  1756. {
  1757.   _logger.info("{flockIWebServiceAccount}.logout()");
  1758.   var c_acct = this._coop.get(this.urn);
  1759.   if (c_acct.isAuthenticated) {
  1760.     try {
  1761.       this.mAPI.logout();
  1762.     } catch (ex) {
  1763.       _logger.warn("API logout call failed with error: "+ex);
  1764.     }
  1765.     Cc[CONTRACT_ID].getService(Ci.flockIWebService).logout();
  1766.     c_acct.isAuthenticated = false;
  1767.   }
  1768. }
  1769.  
  1770. piczoAccount.prototype.activate =
  1771. function piczoAccount_activate(aListener)
  1772. {
  1773.   _logger.info("{flockIWebServiceAccount}.activate()");
  1774.   var c_acct = this._coop.get(this.urn);
  1775.   //c_acct.isTransient = false;
  1776.   var account = this;
  1777.   var loginListener = {
  1778.     onSuccess: function (aSubject, aTopic) {
  1779.       _logger.info(".activate() loginListener.onSuccess()");
  1780.       // XXX TODO FIXME: API login
  1781.       if (aListener) aListener.onSuccess(account, "accountAuthorized");
  1782.       c_acct.isPollable = true;
  1783.       account.acUtils.ensureOnlyAuthenticatedAccount(account.urn);
  1784.     },
  1785.     onError: function (aSubject, aTopic, aError) {
  1786.       _logger.info(".activate() loginListener.onError()");
  1787.       c_acct.isAuthenticated = true;
  1788.     }
  1789.   };
  1790.   //this.acUtils.makeTempPasswordPermanent(this.service.urn+':'+c_acct.accountId);
  1791.   this.login(loginListener);
  1792.   // Assume login will succeed.  Listener will handle failures.
  1793.   c_acct.isAuthenticated = true;
  1794. }
  1795. // END flockIWebServiceAccount interface
  1796.  
  1797. // ========== END piczoAccount class ==========
  1798.  
  1799.  
  1800.  
  1801. // =========================================
  1802. // ========== BEGIN XPCOM Support ==========
  1803. // =========================================
  1804.  
  1805. function createModule(aParams) {
  1806.   return {
  1807.     registerSelf: function (aCompMgr, aFileSpec, aLocation, aType) {
  1808.       aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
  1809.       aCompMgr.registerFactoryLocation( aParams.CID, aParams.componentName,
  1810.                                         aParams.contractID, aFileSpec,
  1811.                                         aLocation, aType );
  1812.       var catMgr = Cc["@mozilla.org/categorymanager;1"]
  1813.         .getService(Ci.nsICategoryManager);
  1814.       if (!aParams.categories) { aParams.categories = []; }
  1815.       for (var i = 0; i < aParams.categories.length; i++) {
  1816.         var cat = aParams.categories[i];
  1817.         catMgr.addCategoryEntry( cat.category, cat.entry,
  1818.                                  cat.value, true, true ); 
  1819.       }
  1820.     },
  1821.     getClassObject: function (aCompMgr, aCID, aIID) {
  1822.       if (!aCID.equals(aParams.CID)) { throw Cr.NS_ERROR_NO_INTERFACE; }
  1823.       if (!aIID.equals(Ci.nsIFactory)) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; }
  1824.       return { // Factory
  1825.         createInstance: function (aOuter, aIID) {
  1826.           if (aOuter != null) { throw Cr.NS_ERROR_NO_AGGREGATION; }
  1827.           var comp = new aParams.componentClass();
  1828.           if (aParams.implementationFunc) { aParams.implementationFunc(comp); }
  1829.           return comp.QueryInterface(aIID);
  1830.         }
  1831.       };
  1832.     },
  1833.     canUnload: function (aCompMgr) { return true; }
  1834.   };
  1835. }
  1836.  
  1837. // NS Module entrypoint
  1838. function NSGetModule(aCompMgr, aFileSpec) {
  1839.   return createModule({
  1840.     componentClass: piczoService,
  1841.     CID: CLASS_ID,
  1842.     contractID: CONTRACT_ID,
  1843.     componentName: CATEGORY_COMPONENT_NAME,
  1844.     implementationFunc: function (aComp) { getCompTK().addAllInterfaces(aComp); aComp.init(); },
  1845.     categories: [ 
  1846.       { category: "wsm-startup", entry: CATEGORY_COMPONENT_NAME, value: CONTRACT_ID },
  1847.       { category: "flockWebService", entry: CATEGORY_ENTRY_NAME, value: CONTRACT_ID },
  1848.       { category: "flockMediaProvider", entry: CATEGORY_ENTRY_NAME, value: CONTRACT_ID }
  1849.     ]
  1850.   });
  1851. }
  1852.  
  1853. // ========== END XPCOM Support ==========
  1854.